package com.modules.common.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.modules.entity.LicenseCreatorParam;
import com.modules.license.LicenseCreator;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

/**
 * KeytoolUtils
 *
 * @author yzz
 * @date 2024-03-29 17:04
 **/
@Slf4j
public class KeytoolUtils {

    private final static String NAME_PRIVATE_KEYS = "privateKeys.keystore";
    private final static String NAME_CERT_FILE = "certFile.cer";
    private final static String NAME_PUBLIC_CERTS = "publicCerts.keystore";
    private final static String NAME_LICENSE = "license.lic";
    private final static String NAME_LICENSE_JSON = "license.json";
    private final static String NAME_LICENSE2_JSON = "license2.json";

    public static void main(String[] args) {
        String path = "E:/workspace/spring-boot2-license/license-file/000000";
        String publicPassword = "public_password1234";
        String privatePassword = "private_password1234";

        //生成证书
        generateKeystore(path, publicPassword, privatePassword);
        String licensePath = getAbsolutePath(path, NAME_LICENSE);
        deleteFile(licensePath);
        String privateKeysPath = getAbsolutePath(path, NAME_PRIVATE_KEYS);
        String certFilePath = getAbsolutePath(path, NAME_CERT_FILE);
        String publicCertsPath = getAbsolutePath(path, NAME_PUBLIC_CERTS);
        String privateKeysBase64 = Base64FileUtils.fileToBase64(privateKeysPath);

        //生成许可证
        String json = "{" +
                "\"subject\": \"程序包版权许可证书\"," +
                "\"privateAlias\": \"privateKey\"," +
                "\"keyPass\": \"" + privatePassword + "\"," +
                "\"storePass\": \"" + publicPassword + "\"," +
                "\"licensePath\": \"" + licensePath + "\"," +
                "\"privateKeysStorePath\": \"" + privateKeysPath + "\"," +
                "\"privateKeysBase64\": \"" + privateKeysBase64 + "\"," +
                "\"issuedTime\": \"2023-05-14 00:00:01\"," +
                "\"expiryTime\": \"2024-05-14 23:59:59\"," +
                "\"consumerType\": \"User\"," +
                "\"consumerAmount\": 1," +
                "\"description\": \"请不要改动此配置，否则程序启动异常。\"," +
                "    \"licenseCheckModel\": {" +
                "        \"tenant\": [" +
                "            \"000000\"," +
                "            \"100000\"" +
                "        ]," +
                "        \"ipAddress\": [" +
                "            \"192.168.245.1\"," +
                "            \"10.0.5.22\"" +
                "        ]," +
                "        \"macAddress\": [" +
                "            \"00-50-56-C0-00-01\"," +
                "            \"50-7B-9D-F9-18-41\"" +
                "        ]," +
                "        \"cpuSerial\": \"BFEBFBFF000406E3\"," +
                "        \"mainBoardSerial\": \"L1HF65E00X9\"" +
                "    }" +
                "}";
        JSONObject licenseJson = JSONObject.parseObject(json);
        LicenseCreatorParam param = licenseJson.toJavaObject(LicenseCreatorParam.class);
        LicenseCreator licenseCreator = new LicenseCreator(param);
        boolean result = licenseCreator.generateLicense();
        log.info("生成许可证结果：{}", result);
        if (result) {
            //打印文件base64
            String license = Base64FileUtils.fileToBase64(licensePath);

            String certFileBase64 = Base64FileUtils.fileToBase64(certFilePath);
            String publicCertsBase64 = Base64FileUtils.fileToBase64(publicCertsPath);
            log.info("{}\n{}", licensePath, license);
            log.info("{}\n{}", privateKeysPath, privateKeysBase64);
            log.info("{}\n{}", certFilePath, certFileBase64);
            log.info("{}\n{}", publicCertsPath, publicCertsBase64);
            licenseJson.put("license", license);
            licenseJson.put("privateKeys", privateKeysBase64);
            licenseJson.put("certFile", certFileBase64);
            licenseJson.put("publicCerts", publicCertsBase64);
            buildLicenseFile(path, licenseJson);
        }
    }

    private static void buildLicenseFile(String path, JSONObject licenseJson) {
        try {
            String licenseJsonPath2 = getAbsolutePath(path, NAME_LICENSE2_JSON);
            deleteFile(licenseJsonPath2);
            FileUtils.writeStringToFile(new File(licenseJsonPath2), JSON.toJSONString(licenseJson, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue,
                    SerializerFeature.WriteDateUseDateFormat), StandardCharsets.UTF_8.name());
            log.info("许可证2生成成功：{}", licenseJsonPath2);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        try {
            licenseJson.remove("privateAlias");
            licenseJson.remove("keyPass");
            licenseJson.remove("storePass");
            licenseJson.remove("licensePath");
            licenseJson.remove("privateKeysStorePath");
            licenseJson.remove("privateKeys");
            licenseJson.remove("certFile");
            licenseJson.remove("publicCerts");

            String licenseJsonPath = getAbsolutePath(path, NAME_LICENSE_JSON);
            deleteFile(licenseJsonPath);
            FileUtils.writeStringToFile(new File(licenseJsonPath), JSON.toJSONString(licenseJson, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue,
                    SerializerFeature.WriteDateUseDateFormat), StandardCharsets.UTF_8.name());
            log.info("许可证生成成功：{}", licenseJsonPath);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void generateKeystore(String path, String publicPassword, String privatePassword) {
        File dir = new File(path);
        if (!dir.exists()) {
            boolean success = dir.mkdirs();
            if (!success) {
                throw new RuntimeException(path + "目录创建失败");
            }
        }
        path = dir.getAbsolutePath().replace("\\", "/");
        String privateKeysPath = getAbsolutePath(path, NAME_PRIVATE_KEYS);
        deleteFile(privateKeysPath);
        String certFilePath = getAbsolutePath(path, NAME_CERT_FILE);
        deleteFile(certFilePath);
        String publicCertsPath = getAbsolutePath(path, NAME_PUBLIC_CERTS);
        deleteFile(publicCertsPath);
        String command = String.format("keytool -genkeypair -keyalg DSA -keysize 1024 -validity 365000 -alias \"privateKey\" -keystore \"%s\" -storepass \"%s\" -keypass \"%s\" -dname \"CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN\"", privateKeysPath, publicPassword, privatePassword);
        exec(command);
        command = String.format("keytool -exportcert -keyalg DSA -alias \"privateKey\" -keystore \"%s\" -storepass \"%s\" -file \"%s\"", privateKeysPath, publicPassword, certFilePath);
        exec(command);
        command = String.format("keytool -import -keyalg DSA -alias \"publicCert\" -file \"%s\" -keystore \"%s\" -storepass \"%s\" -trustcacerts -noprompt", certFilePath, publicCertsPath, publicPassword);
        exec(command);
    }

    private static void exec(String command) {
        Runtime runtime = null;
        Process process = null;
        BufferedReader reader = null;
        try {
            log.info("开始指令执行：{}", command);
            runtime = Runtime.getRuntime();
            process = runtime.exec(command);

            // 读取命令执行结果
            reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8));
            String line;
            while ((line = reader.readLine()) != null) {
                log.info("指令执行结果：{}", line);
            }
            // 等待命令执行完成
            process.waitFor();

            // 获取退出值
            int exitValue = process.exitValue();

            log.info("结束指令执行：{}", exitValue);
        } catch (IOException | InterruptedException e) {
            log.error("指令执行异常", e);
            throw new RuntimeException(e);
        } finally {
            if (Objects.nonNull(reader)) {
                try {
                    reader.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if (Objects.nonNull(runtime)) {
                runtime.freeMemory();
            }
            if (Objects.nonNull(process)) {
                process.destroy();
            }
        }
    }

    private static String getAbsolutePath(String s1, String s2) {
        return String.format("%s/%s", s1, s2);
    }

    private static void deleteFile(String s) {
        File file = new File(s);
        if (file.exists()) {
            boolean success = file.delete();
            if (!success) {
                throw new RuntimeException(s + "文件删除失败");
            }
        }
    }


}
